home *** CD-ROM | disk | FTP | other *** search
/ Freelog 125 / Freelog_MarsAvril2015_No125.iso / ViePratique / ArchiFacile / ArchiFacileSetup.exe / {app} / nw.pak / Unnamed File 000115.txt < prev    next >
Text File  |  2014-10-14  |  18KB  |  515 lines

  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4.  
  5. // require: event_tracker.js
  6.  
  7. cr.define('cr.ui', function() {
  8.  
  9.   /**
  10.    * The arrow location specifies how the arrow and bubble are positioned in
  11.    * relation to the anchor node.
  12.    * @enum
  13.    */
  14.   var ArrowLocation = {
  15.     // The arrow is positioned at the top and the start of the bubble. In left
  16.     // to right mode this is the top left. The entire bubble is positioned below
  17.     // the anchor node.
  18.     TOP_START: 'top-start',
  19.     // The arrow is positioned at the top and the end of the bubble. In left to
  20.     // right mode this is the top right. The entire bubble is positioned below
  21.     // the anchor node.
  22.     TOP_END: 'top-end',
  23.     // The arrow is positioned at the bottom and the start of the bubble. In
  24.     // left to right mode this is the bottom left. The entire bubble is
  25.     // positioned above the anchor node.
  26.     BOTTOM_START: 'bottom-start',
  27.     // The arrow is positioned at the bottom and the end of the bubble. In
  28.     // left to right mode this is the bottom right. The entire bubble is
  29.     // positioned above the anchor node.
  30.     BOTTOM_END: 'bottom-end'
  31.   };
  32.  
  33.   /**
  34.    * The bubble alignment specifies the position of the bubble in relation to
  35.    * the anchor node.
  36.    * @enum
  37.    */
  38.   var BubbleAlignment = {
  39.     // The bubble is positioned just above or below the anchor node (as
  40.     // specified by the arrow location) so that the arrow points at the midpoint
  41.     // of the anchor.
  42.     ARROW_TO_MID_ANCHOR: 'arrow-to-mid-anchor',
  43.     // The bubble is positioned just above or below the anchor node (as
  44.     // specified by the arrow location) so that its reference edge lines up with
  45.     // the edge of the anchor.
  46.     BUBBLE_EDGE_TO_ANCHOR_EDGE: 'bubble-edge-anchor-edge',
  47.     // The bubble is positioned so that it is entirely within view and does not
  48.     // obstruct the anchor element, if possible. The specified arrow location is
  49.     // taken into account as the preferred alignment but may be overruled if
  50.     // there is insufficient space (see BubbleBase.reposition for the exact
  51.     // placement algorithm).
  52.     ENTIRELY_VISIBLE: 'entirely-visible'
  53.   };
  54.  
  55.   /**
  56.    * Abstract base class that provides common functionality for implementing
  57.    * free-floating informational bubbles with a triangular arrow pointing at an
  58.    * anchor node.
  59.    */
  60.   var BubbleBase = cr.ui.define('div');
  61.  
  62.   /**
  63.    * The horizontal distance between the tip of the arrow and the reference edge
  64.    * of the bubble (as specified by the arrow location). In pixels.
  65.    * @type {number}
  66.    * @const
  67.    */
  68.   BubbleBase.ARROW_OFFSET = 30;
  69.  
  70.   /**
  71.    * Minimum horizontal spacing between edge of bubble and edge of viewport
  72.    * (when using the ENTIRELY_VISIBLE alignment). In pixels.
  73.    * @type {number}
  74.    * @const
  75.    */
  76.   BubbleBase.MIN_VIEWPORT_EDGE_MARGIN = 2;
  77.  
  78.   BubbleBase.prototype = {
  79.     // Set up the prototype chain.
  80.     __proto__: HTMLDivElement.prototype,
  81.  
  82.     /**
  83.      * Initialization function for the cr.ui framework.
  84.      */
  85.     decorate: function() {
  86.       this.className = 'bubble';
  87.       this.innerHTML =
  88.           '<div class="bubble-content"></div>' +
  89.           '<div class="bubble-shadow"></div>' +
  90.           '<div class="bubble-arrow"></div>';
  91.       this.hidden = true;
  92.       this.bubbleAlignment = BubbleAlignment.ENTIRELY_VISIBLE;
  93.     },
  94.  
  95.     /**
  96.      * Set the anchor node, i.e. the node that this bubble points at. Only
  97.      * available when the bubble is not being shown.
  98.      * @param {HTMLElement} node The new anchor node.
  99.      */
  100.     set anchorNode(node) {
  101.       if (!this.hidden)
  102.         return;
  103.  
  104.       this.anchorNode_ = node;
  105.     },
  106.  
  107.     /**
  108.      * Set the conent of the bubble. Only available when the bubble is not being
  109.      * shown.
  110.      * @param {HTMLElement} node The root node of the new content.
  111.      */
  112.     set content(node) {
  113.       if (!this.hidden)
  114.         return;
  115.  
  116.       var bubbleContent = this.querySelector('.bubble-content');
  117.       bubbleContent.innerHTML = '';
  118.       bubbleContent.appendChild(node);
  119.     },
  120.  
  121.     /**
  122.      * Set the arrow location. Only available when the bubble is not being
  123.      * shown.
  124.      * @param {cr.ui.ArrowLocation} location The new arrow location.
  125.      */
  126.     set arrowLocation(location) {
  127.       if (!this.hidden)
  128.         return;
  129.  
  130.       this.arrowAtRight_ = location == ArrowLocation.TOP_END ||
  131.                            location == ArrowLocation.BOTTOM_END;
  132.       if (document.documentElement.dir == 'rtl')
  133.         this.arrowAtRight_ = !this.arrowAtRight_;
  134.       this.arrowAtTop_ = location == ArrowLocation.TOP_START ||
  135.                          location == ArrowLocation.TOP_END;
  136.     },
  137.  
  138.     /**
  139.      * Set the bubble alignment. Only available when the bubble is not being
  140.      * shown.
  141.      * @param {cr.ui.BubbleAlignment} alignment The new bubble alignment.
  142.      */
  143.     set bubbleAlignment(alignment) {
  144.       if (!this.hidden)
  145.         return;
  146.  
  147.       this.bubbleAlignment_ = alignment;
  148.     },
  149.  
  150.     /**
  151.      * Update the position of the bubble. Whenever the layout may have changed,
  152.      * the bubble should either be repositioned by calling this function or
  153.      * hidden so that it does not point to a nonsensical location on the page.
  154.      */
  155.     reposition: function() {
  156.       var documentWidth = document.documentElement.clientWidth;
  157.       var documentHeight = document.documentElement.clientHeight;
  158.       var anchor = this.anchorNode_.getBoundingClientRect();
  159.       var anchorMid = (anchor.left + anchor.right) / 2;
  160.       var bubble = this.getBoundingClientRect();
  161.       var arrow = this.querySelector('.bubble-arrow').getBoundingClientRect();
  162.  
  163.       if (this.bubbleAlignment_ == BubbleAlignment.ENTIRELY_VISIBLE) {
  164.         // Work out horizontal placement. The bubble is initially positioned so
  165.         // that the arrow tip points toward the midpoint of the anchor and is
  166.         // BubbleBase.ARROW_OFFSET pixels from the reference edge and (as
  167.         // specified by the arrow location). If the bubble is not entirely
  168.         // within view, it is then shifted, preserving the arrow tip position.
  169.         var left = this.arrowAtRight_ ?
  170.            anchorMid + BubbleBase.ARROW_OFFSET - bubble.width :
  171.            anchorMid - BubbleBase.ARROW_OFFSET;
  172.         var max_left_pos =
  173.             documentWidth - bubble.width - BubbleBase.MIN_VIEWPORT_EDGE_MARGIN;
  174.         var min_left_pos = BubbleBase.MIN_VIEWPORT_EDGE_MARGIN;
  175.         if (document.documentElement.dir == 'rtl')
  176.           left = Math.min(Math.max(left, min_left_pos), max_left_pos);
  177.         else
  178.           left = Math.max(Math.min(left, max_left_pos), min_left_pos);
  179.         var arrowTip = Math.min(
  180.             Math.max(arrow.width / 2,
  181.                      this.arrowAtRight_ ? left + bubble.width - anchorMid :
  182.                                           anchorMid - left),
  183.             bubble.width - arrow.width / 2);
  184.  
  185.         // Work out the vertical placement, attempting to fit the bubble
  186.         // entirely into view. The following placements are considered in
  187.         // decreasing order of preference:
  188.         // * Outside the anchor, arrow tip touching the anchor (arrow at
  189.         //   top/bottom as specified by the arrow location).
  190.         // * Outside the anchor, arrow tip touching the anchor (arrow at
  191.         //   bottom/top, opposite the specified arrow location).
  192.         // * Outside the anchor, arrow tip overlapping the anchor (arrow at
  193.         //   top/bottom as specified by the arrow location).
  194.         // * Outside the anchor, arrow tip overlapping the anchor (arrow at
  195.         //   bottom/top, opposite the specified arrow location).
  196.         // * Overlapping the anchor.
  197.         var offsetTop = Math.min(documentHeight - anchor.bottom - bubble.height,
  198.                                  arrow.height / 2);
  199.         var offsetBottom = Math.min(anchor.top - bubble.height,
  200.                                     arrow.height / 2);
  201.         if (offsetTop < 0 && offsetBottom < 0) {
  202.           var top = 0;
  203.           this.updateArrowPosition_(false, false, arrowTip);
  204.         } else if (offsetTop > offsetBottom ||
  205.                    offsetTop == offsetBottom && this.arrowAtTop_) {
  206.           var top = anchor.bottom + offsetTop;
  207.           this.updateArrowPosition_(true, true, arrowTip);
  208.         } else {
  209.           var top = anchor.top - bubble.height - offsetBottom;
  210.           this.updateArrowPosition_(true, false, arrowTip);
  211.         }
  212.       } else {
  213.         if (this.bubbleAlignment_ ==
  214.             BubbleAlignment.BUBBLE_EDGE_TO_ANCHOR_EDGE) {
  215.           var left = this.arrowAtRight_ ? anchor.right - bubble.width :
  216.               anchor.left;
  217.         } else {
  218.           var left = this.arrowAtRight_ ?
  219.               anchorMid - this.clientWidth + BubbleBase.ARROW_OFFSET :
  220.               anchorMid - BubbleBase.ARROW_OFFSET;
  221.         }
  222.         var top = this.arrowAtTop_ ? anchor.bottom + arrow.height / 2 :
  223.             anchor.top - this.clientHeight - arrow.height / 2;
  224.         this.updateArrowPosition_(true, this.arrowAtTop_,
  225.                                   BubbleBase.ARROW_OFFSET);
  226.       }
  227.  
  228.       this.style.left = left + 'px';
  229.       this.style.top = top + 'px';
  230.     },
  231.  
  232.     /**
  233.      * Show the bubble.
  234.      */
  235.     show: function() {
  236.       if (!this.hidden)
  237.         return;
  238.  
  239.       this.attachToDOM_();
  240.       this.hidden = false;
  241.       this.reposition();
  242.  
  243.       var doc = this.ownerDocument;
  244.       this.eventTracker_ = new EventTracker;
  245.       this.eventTracker_.add(doc, 'keydown', this, true);
  246.       this.eventTracker_.add(doc, 'mousedown', this, true);
  247.     },
  248.  
  249.     /**
  250.      * Hide the bubble.
  251.      */
  252.     hide: function() {
  253.       if (this.hidden)
  254.         return;
  255.  
  256.       this.eventTracker_.removeAll();
  257.       this.hidden = true;
  258.       this.parentNode.removeChild(this);
  259.     },
  260.  
  261.     /**
  262.      * Handle keyboard events, dismissing the bubble if necessary.
  263.      * @param {Event} event The event.
  264.      */
  265.     handleEvent: function(event) {
  266.       // Close the bubble when the user presses <Esc>.
  267.       if (event.type == 'keydown' && event.keyCode == 27) {
  268.         this.hide();
  269.         event.preventDefault();
  270.         event.stopPropagation();
  271.       }
  272.     },
  273.  
  274.     /**
  275.      * Attach the bubble to the document's DOM.
  276.      * @private
  277.      */
  278.     attachToDOM_: function() {
  279.       document.body.appendChild(this);
  280.     },
  281.  
  282.     /**
  283.      * Update the arrow so that it appears at the correct position.
  284.      * @param {boolean} visible Whether the arrow should be visible.
  285.      * @param {boolean} atTop Whether the arrow should be at the top of the
  286.      * bubble.
  287.      * @param {number} tipOffset The horizontal distance between the tip of the
  288.      * arrow and the reference edge of the bubble (as specified by the arrow
  289.      * location).
  290.      * @private
  291.      */
  292.     updateArrowPosition_: function(visible, atTop, tipOffset) {
  293.       var bubbleArrow = this.querySelector('.bubble-arrow');
  294.       bubbleArrow.hidden = !visible;
  295.       if (!visible)
  296.         return;
  297.  
  298.       var edgeOffset = (-bubbleArrow.clientHeight / 2) + 'px';
  299.       bubbleArrow.style.top = atTop ? edgeOffset : 'auto';
  300.       bubbleArrow.style.bottom = atTop ? 'auto' : edgeOffset;
  301.  
  302.       edgeOffset = (tipOffset - bubbleArrow.offsetWidth / 2) + 'px';
  303.       bubbleArrow.style.left = this.arrowAtRight_ ? 'auto' : edgeOffset;
  304.       bubbleArrow.style.right = this.arrowAtRight_ ? edgeOffset : 'auto';
  305.     },
  306.   };
  307.  
  308.   /**
  309.    * A bubble that remains open until the user explicitly dismisses it or clicks
  310.    * outside the bubble after it has been shown for at least the specified
  311.    * amount of time (making it less likely that the user will unintentionally
  312.    * dismiss the bubble). The bubble repositions itself on layout changes.
  313.    */
  314.   var Bubble = cr.ui.define('div');
  315.  
  316.   Bubble.prototype = {
  317.     // Set up the prototype chain.
  318.     __proto__: BubbleBase.prototype,
  319.  
  320.     /**
  321.      * Initialization function for the cr.ui framework.
  322.      */
  323.     decorate: function() {
  324.       BubbleBase.prototype.decorate.call(this);
  325.  
  326.       var close = document.createElement('div');
  327.       close.className = 'bubble-close';
  328.       this.insertBefore(close, this.querySelector('.bubble-content'));
  329.  
  330.       this.handleCloseEvent = this.hide;
  331.       this.deactivateToDismissDelay_ = 0;
  332.       this.bubbleAlignment = BubbleAlignment.ARROW_TO_MID_ANCHOR;
  333.     },
  334.  
  335.     /**
  336.      * Handler for close events triggered when the close button is clicked. By
  337.      * default, set to this.hide. Only available when the bubble is not being
  338.      * shown.
  339.      * @param {function} handler The new handler, a function with no parameters.
  340.      */
  341.     set handleCloseEvent(handler) {
  342.       if (!this.hidden)
  343.         return;
  344.  
  345.       this.handleCloseEvent_ = handler;
  346.     },
  347.  
  348.     /**
  349.      * Set the delay before the user is allowed to click outside the bubble to
  350.      * dismiss it. Using a delay makes it less likely that the user will
  351.      * unintentionally dismiss the bubble.
  352.      * @param {number} delay The delay in milliseconds.
  353.      */
  354.     set deactivateToDismissDelay(delay) {
  355.       this.deactivateToDismissDelay_ = delay;
  356.     },
  357.  
  358.     /**
  359.      * Hide or show the close button.
  360.      * @param {boolean} isVisible True if the close button should be visible.
  361.      */
  362.     set closeButtonVisible(isVisible) {
  363.       this.querySelector('.bubble-close').hidden = !isVisible;
  364.     },
  365.  
  366.     /**
  367.      * Show the bubble.
  368.      */
  369.     show: function() {
  370.       if (!this.hidden)
  371.         return;
  372.  
  373.       BubbleBase.prototype.show.call(this);
  374.  
  375.       this.showTime_ = Date.now();
  376.       this.eventTracker_.add(window, 'resize', this.reposition.bind(this));
  377.     },
  378.  
  379.     /**
  380.      * Handle keyboard and mouse events, dismissing the bubble if necessary.
  381.      * @param {Event} event The event.
  382.      */
  383.     handleEvent: function(event) {
  384.       BubbleBase.prototype.handleEvent.call(this, event);
  385.  
  386.       if (event.type == 'mousedown') {
  387.         // Dismiss the bubble when the user clicks on the close button.
  388.         if (event.target == this.querySelector('.bubble-close')) {
  389.           this.handleCloseEvent_();
  390.         // Dismiss the bubble when the user clicks outside it after the
  391.         // specified delay has passed.
  392.         } else if (!this.contains(event.target) &&
  393.             Date.now() - this.showTime_ >= this.deactivateToDismissDelay_) {
  394.           this.hide();
  395.         }
  396.       }
  397.     },
  398.   };
  399.  
  400.   /**
  401.    * A bubble that closes automatically when the user clicks or moves the focus
  402.    * outside the bubble and its target element, scrolls the underlying document
  403.    * or resizes the window.
  404.    */
  405.   var AutoCloseBubble = cr.ui.define('div');
  406.  
  407.   AutoCloseBubble.prototype = {
  408.     // Set up the prototype chain.
  409.     __proto__: BubbleBase.prototype,
  410.  
  411.     /**
  412.      * Initialization function for the cr.ui framework.
  413.      */
  414.     decorate: function() {
  415.       BubbleBase.prototype.decorate.call(this);
  416.       this.classList.add('auto-close-bubble');
  417.     },
  418.  
  419.     /**
  420.      * Set the DOM sibling node, i.e. the node as whose sibling the bubble
  421.      * should join the DOM to ensure that focusable elements inside the bubble
  422.      * follow the target element in the document's tab order. Only available
  423.      * when the bubble is not being shown.
  424.      * @param {HTMLElement} node The new DOM sibling node.
  425.      */
  426.     set domSibling(node) {
  427.       if (!this.hidden)
  428.         return;
  429.  
  430.       this.domSibling_ = node;
  431.     },
  432.  
  433.     /**
  434.      * Show the bubble.
  435.      */
  436.     show: function() {
  437.       if (!this.hidden)
  438.         return;
  439.  
  440.       BubbleBase.prototype.show.call(this);
  441.       this.domSibling_.showingBubble = true;
  442.  
  443.       var doc = this.ownerDocument;
  444.       this.eventTracker_.add(doc, 'mousewheel', this, true);
  445.       this.eventTracker_.add(doc, 'scroll', this, true);
  446.       this.eventTracker_.add(doc, 'elementFocused', this, true);
  447.       this.eventTracker_.add(window, 'resize', this);
  448.     },
  449.  
  450.     /**
  451.      * Hide the bubble.
  452.      */
  453.     hide: function() {
  454.       BubbleBase.prototype.hide.call(this);
  455.       this.domSibling_.showingBubble = false;
  456.     },
  457.  
  458.     /**
  459.      * Handle events, closing the bubble when the user clicks or moves the focus
  460.      * outside the bubble and its target element, scrolls the underlying
  461.      * document or resizes the window.
  462.      * @param {Event} event The event.
  463.      */
  464.     handleEvent: function(event) {
  465.       BubbleBase.prototype.handleEvent.call(this, event);
  466.  
  467.       switch (event.type) {
  468.         // Close the bubble when the user clicks outside it, except if it is a
  469.         // left-click on the bubble's target element (allowing the target to
  470.         // handle the event and close the bubble itself).
  471.         case 'mousedown':
  472.           if (event.button == 0 && this.anchorNode_.contains(event.target))
  473.             break;
  474.         // Close the bubble when the underlying document is scrolled.
  475.         case 'mousewheel':
  476.         case 'scroll':
  477.           if (this.contains(event.target))
  478.             break;
  479.         // Close the bubble when the window is resized.
  480.         case 'resize':
  481.           this.hide();
  482.           break;
  483.         // Close the bubble when the focus moves to an element that is not the
  484.         // bubble target and is not inside the bubble.
  485.         case 'elementFocused':
  486.           if (!this.anchorNode_.contains(event.target) &&
  487.               !this.contains(event.target)) {
  488.             this.hide();
  489.           }
  490.           break;
  491.       }
  492.     },
  493.  
  494.     /**
  495.      * Attach the bubble to the document's DOM, making it a sibling of the
  496.      * |domSibling_| so that focusable elements inside the bubble follow the
  497.      * target element in the document's tab order.
  498.      * @private
  499.      */
  500.     attachToDOM_: function() {
  501.       var parent = this.domSibling_.parentNode;
  502.       parent.insertBefore(this, this.domSibling_.nextSibling);
  503.     },
  504.   };
  505.  
  506.  
  507.   return {
  508.     ArrowLocation: ArrowLocation,
  509.     BubbleAlignment: BubbleAlignment,
  510.     BubbleBase: BubbleBase,
  511.     Bubble: Bubble,
  512.     AutoCloseBubble: AutoCloseBubble
  513.   };
  514. });
  515.